/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package fileManagers;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.Serializable;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import umat.beans.Node;
import umat.beans.Tree;

/**
 *
 * @author Roxana Radulescu
 */
public class XMLController implements Serializable {

    private Tree tree;
    private DocumentBuilderFactory factory;
    private DocumentBuilder docBuilder;
    private Document doc;
    private File file;

    public XMLController() {
        try {
            factory = DocumentBuilderFactory.newInstance();  //Get the DocumentBuilder
            docBuilder = factory.newDocumentBuilder();              //Create blank DOM Document
            file = null;
        } catch (ParserConfigurationException ex) {
            Logger.getLogger(XMLController.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public XMLController(Tree tree) {
        this.tree = tree;
        try {

            factory = DocumentBuilderFactory.newInstance();  //Get the DocumentBuilder
            docBuilder = factory.newDocumentBuilder();       //Create blank DOM Document        

        } catch (ParserConfigurationException ex) {
            Logger.getLogger(XMLController.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void parseToXML() {
        try {
            doc = docBuilder.newDocument(); //create a new document
            if (tree == null) {
                tree = findBean("tree");//find the tree created by the admin
            }
            Element root = getDoc().createElement(getTree().getRoot().getData().toString());
            getDoc().appendChild(root);

            if (getTree().getRoot().getFieldSize() > 0) {
                parseFields(root, (Node) getTree().getRoot());
            }

            if (getTree().getRoot().getChildCount() > 0) {
                for (int i = 0; i < getTree().getRoot().getChildCount(); i++) {
                    parseNodes(root, (Node) getTree().getRoot().getChildren().get(i));
                }
            }

            //transform the doc to xml
            TransformerFactory transformerFactory = TransformerFactory.newInstance();
            Transformer transformer = transformerFactory.newTransformer();
            //this output property makes each tag start on a new line
            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
            DOMSource source = new DOMSource(getDoc());

            //creating the file, named after the roots node
            file = new File(getTree().getRoot().getData().toString() + ".xml");
            StreamResult result = new StreamResult(file);
            transformer.transform(source, result);

        } catch (TransformerException ex) {
            Logger.getLogger(XMLController.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private void parseNodes(Element e, Node n) {
        Element aux = getDoc().createElement("node");
        e.appendChild(aux);
        Attr attr = getDoc().createAttribute("name");
        attr.setValue(n.getData().toString());
        aux.setAttributeNode(attr);

        if (n.getFieldSize() > 0) {
            parseFields(aux, n);
        }
        if (n.getChildCount() > 0) {
            for (int i = 0; i < n.getChildCount(); i++) {
                parseNodes(aux, (Node) n.getChildren().get(i));
            }
        }
    }

    private void parseFields(Element e, Node n) {
        for (int i = 0; i < n.getFieldSize(); i++) {
            Element aux = getDoc().createElement("field");
            e.appendChild(aux);

            Attr attr1 = getDoc().createAttribute("name");
            attr1.setValue(n.getFields().get(i).getName());
            aux.setAttributeNode(attr1);

            Attr attr2 = getDoc().createAttribute("type");
            attr2.setValue(n.getFields().get(i).getType());
            aux.setAttributeNode(attr2);
        }
    }

    @SuppressWarnings("unchecked")
    private static <T> T findBean(String beanName) {
        FacesContext context = FacesContext.getCurrentInstance();
        return (T) context.getApplication().evaluateExpressionGet(context, "#{" + beanName + "}", Object.class);
    }

    public File getFile() throws IOException {
        parseToXML();
        FacesContext facesContext = FacesContext.getCurrentInstance();
        ExternalContext externalContext = facesContext.getExternalContext();
        HttpServletResponse response = (HttpServletResponse) externalContext.getResponse();

        response.reset();                           // Some JSF component library or some Filter might have set some headers in the buffer beforehand. We want to get rid of them, else it may collide.
        response.setContentType("application/xml"); // Check http://www.w3schools.com/media/media_mimeref.asp for all types. Use if necessary ServletContext#getMimeType() for auto-detection based on filename.
        response.setHeader("Content-disposition", "attachment; filename=" + file.getName());

        BufferedInputStream input = null;
        BufferedOutputStream output = null;

        try {
            input = new BufferedInputStream(new FileInputStream(file));
            output = new BufferedOutputStream(response.getOutputStream());

            byte[] buffer = new byte[10240];
            for (int length; (length = input.read(buffer)) > 0;) {
                output.write(buffer, 0, length);
            }
        } finally {
            output.close();
            input.close();
        }
        facesContext.responseComplete();
        return file;
    }

    /**
     * @return the tree
     */
    public Tree getTree() {
        return tree;
    }

    /**
     * @return the factory
     */
    public DocumentBuilderFactory getFactory() {
        return factory;
    }

    /**
     * @return the docBuilder
     */
    public DocumentBuilder getDocBuilder() {
        return docBuilder;
    }

    /**
     * @return the doc
     */
    public Document getDoc() {
        return doc;
    }

    /**
     * @param tree the tree to set
     */
    public void setTree(Tree tree) {
        this.tree = tree;
    }

    /**
     * @param factory the factory to set
     */
    public void setFactory(DocumentBuilderFactory factory) {
        this.factory = factory;
    }

    /**
     * @param docBuilder the docBuilder to set
     */
    public void setDocBuilder(DocumentBuilder docBuilder) {
        this.docBuilder = docBuilder;
    }

    /**
     * @param doc the doc to set
     */
    public void setDoc(Document doc) {
        this.doc = doc;
    }

    /**
     * @param file the file to set
     */
    public void setFile(File file) {
        this.file = file;
    }
}
